<template>
	<div ref="itemTypeTreeRef" class="item-type-tree">
		<div
			v-for="(item, index) in allVisibleData"
			:class="[selectItemClasses(item),
				'tree-node']"
			:key="item.id"
			clickable
			@click="onCellClick(item)"
		>
			<w-render-dom
				v-if="optionRender"
				:render="optionRender"
				:backValue="{item, index}"
			></w-render-dom>
			<div v-else class="fx-ellipsis">{{ item[optionProp.name as keyof OptionItem] }}</div>
			<w-icon-svg v-show="hasChildren(item)" class="cell-link-arrow" type="icon-cell-link-arrow"></w-icon-svg>
		</div>
	</div>
</template>

<script lang="ts">
import { defineComponent, ref, computed, watch } from 'vue'
import { getFxInstance } from '@/js/instance'
import type { PropType } from 'vue'
interface OptionItem {
	id: string
	name: string
	label: string
	disabled: boolean
	visibleFlag: boolean
	children: OptionItem[]
	level: number
	expanded: boolean
	_visible: boolean
	isSelectChild: boolean
}
type ParentNode = Partial<OptionItem>
interface NodeParentMap {
	id: string
	parent: ParentNode
}
export default defineComponent({
	name: 'itemTypeTree',
	props: {
		selectedTypeData: {
			type: Object as PropType<OptionItem>,
			default: () => {
				return {}
			}
		},
		optionlist: {
			type: Array as PropType<OptionItem[]>,
			default: () => {
				return []
			}
		},
		optionProp: {
			type: Object,
			default: () => {
				return {
					name: 'name',
					disabled: 'disabled',
					visibleFlag: 'visibleFlag',
					childKey: 'children'
				}
			}
		},
		optionRender: Function,
		allTypeSelectData: {
			type: Array as PropType<string[]>,
			default: () => {
				return []
			}
		},
		nodeClick: Function
	},
	emits: ['update:selectedTypeData', 'update:allTypeSelectData'],
	setup (props, { emit }) {
		const fxInstance = getFxInstance()
		const allVisibleData = ref<OptionItem[]>([])
		const nodeParentMap = ref<Record<string, NodeParentMap>>({})
		const optionPropChildKey = computed<keyof OptionItem>(() => {
			return props.optionProp.childKey || 'children'
		})
		const optionPropDisabledKey = computed<keyof OptionItem>(() => {
			return props.optionProp.disabled || 'disabled'
		})
		const optionPropVisibleFlagKey = computed<keyof OptionItem>(() => {
			return props.optionProp.visibleFlag || 'visibleFlag'
		})
		const flattenTree = computed(() => {
			const flatten = (
				list: OptionItem[],
				childKey: keyof OptionItem,
				level = 1,
				parent: ParentNode,
				defaultExpand = false
			) => {
				const arr: OptionItem[] = []
				list.forEach(item => {
					item.level = level
					if (item.expanded === undefined) {
						item.expanded = defaultExpand
					}
					if (item._visible === undefined) {
						item._visible = true
					}
					if (item.isSelectChild === undefined) {
						item.isSelectChild = false
					}
					if (!parent._visible || !parent.expanded) {
						item._visible = false
					}
					nodeParentMap.value[item.id] = {
						id: item.id,
						parent
					}
					arr.push(item)
					if (item[childKey]) {
						arr.push(
							...flatten(
								item[childKey] as OptionItem[],
								childKey,
								level + 1,
								item,
								defaultExpand
							)
						)
					}
				})
				return arr
			}
			return flatten(props.optionlist, props.optionProp.childKey || 'children', 1, {
				level: 0,
				_visible: true,
				expanded: true,
				children: props.optionlist
			})
		})
		const selectItemClasses = computed(() => {
			return (listItem: OptionItem) => {
				return {
					selected: listItem.id === props.selectedTypeData!.id,
					'has-children': listItem[optionPropChildKey.value],
					'is-expanded': listItem.expanded,
					'is-disabled': !!listItem[optionPropDisabledKey.value],
					'is-select-child': listItem.isSelectChild
				}
			}
		})
		const hasChildren = computed(() => {
			return (listItem: OptionItem) => {
				return	listItem[optionPropChildKey.value]
			}
		})
		watch(() => {
			return props.optionlist
		}, () => {
			nodeParentMap.value = {}
			updateAllVisibleData()
			scrollTop()
		})
		watch(() => {
			return props.selectedTypeData
		}, (val: OptionItem) => {
			expand(val)
			updateAllVisibleData()
			const allTypeSelectData = getAllChildrenIds(val)
			allTypeSelectData.push(val.id)
			emit('update:allTypeSelectData', allTypeSelectData)
		})
		const onCellClick = (optionItem: OptionItem) => {
			if (optionItem[optionPropDisabledKey.value]) {
				return false
			}
			if (props.nodeClick && fxInstance.$fxUtils.isFunction(props.nodeClick)) {
				props.nodeClick(optionItem)
			} else {
				emit('update:selectedTypeData', optionItem)
			}
		}
		const updateAllVisibleData = () => {
			allVisibleData.value = (flattenTree.value || []).filter(item => item._visible && item[optionPropVisibleFlagKey.value] !== false)
		}
		const getAllChildrenIds = (selectedTypeData: OptionItem) => {
			const allChildrenIds: string[] = []
			const traverse = (data: OptionItem) => {
				const childDatas = (data[optionPropChildKey.value] || []) as OptionItem[]
				childDatas.forEach(child => {
					allChildrenIds.push(child.id)
					traverse(child)
				})
			}
			traverse(selectedTypeData)
			return allChildrenIds
		}
		const expand = (item: OptionItem) => {
			item.expanded = true
			const parentItem = nodeParentMap.value[item.id].parent
			parentItem.expanded = true
			parentItem.isSelectChild = false
			const parentItemChild = (parentItem[optionPropChildKey.value] || []) as OptionItem[]
			parentItemChild.forEach(child => {
				child.isSelectChild = false
				if (child.id !== item.id) {
					collapse(child)
				}
			})
			const children = (item[optionPropChildKey.value] || []) as OptionItem[]
			children.forEach(child => {
				child._visible = true
				child.isSelectChild = true
				collapse(child)
			})
		}
		const collapse = (item: OptionItem) => {
			item.expanded = false
			recursionVisible(item[optionPropChildKey.value] as OptionItem[], false)
		}
		// 递归节点
		const recursionVisible = (children: OptionItem[] = [], status: boolean) => {
			children.forEach(node => {
				node.expanded = status
				node._visible = status
				node.isSelectChild = status
				if (node[optionPropChildKey.value]) {
					recursionVisible(node[optionPropChildKey.value] as OptionItem[], status)
				}
			})
		}
		const expandParentNode = (item: ParentNode) => {
			const parentItem = nodeParentMap.value[item.id!].parent
			if (parentItem.id) {
				parentItem.children?.forEach(item => {
					item._visible = true
				})
				parentItem.expanded = true
				parentItem._visible = true
				parentItem.isSelectChild = false
				expandParentNode(parentItem)
			}
		}
		const itemTypeTreeRef = ref<HTMLElement>()
		const expandNode = (id: string) => {
			const node = flattenTree.value.find(item => item.id === id)
			if (node) {
				node._visible = true
				expand(node)
				expandParentNode(node)
				updateAllVisibleData()
			}
		}
		const scrollTop = () => {
			itemTypeTreeRef.value!.scrollTop = 0
		}
		const getFlattenTree = () => {
			return flattenTree.value
		}
		return {
			updateAllVisibleData,
			scrollTop,
			allVisibleData,
			selectItemClasses,
			hasChildren,
			onCellClick,
			itemTypeTreeRef,
			expandNode,
			getFlattenTree
		}
	}
})
</script>

<style lang="scss" scoped>
@import "$assets/stylus/varsty";
.item-type-tree {
	position: relative;
	flex: 1;
	.tree-node {
		position: relative;
		padding-right: 15px;
		padding-left: 6px;
		height: 60px;
		line-height: 60px;
	}
	.selected {
		color: $fxBlue;
		&::before {
			content: '';
			position: absolute;
			top: 5px;
			left: 0;
			width: 3px;
			height: 50px;
			background-color: $fxBlue;
		}
	}
	.is-select-child {
		&::before {
			content: '';
			position: absolute;
			top: 7px;
			left: 0;
			width: 2px;
			height: 46px;
			background-color: $fxBlue;
		}
	}
	.has-children {
		.cell-link-arrow {
			position: absolute;
			top: 50%;
			right: 3px;
			width: 10px;
			height: 10px;
			transform: translateY(-50%);
		}
	}
	.is-expanded {
		.cell-link-arrow {
			transform: translateY(-50%) rotate(90deg);
		}
	}
	.is-disabled {
		color: $fxBlack2;
	}
}
</style>
